#include "genericv.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>

char myatoc(char *ptr)
{
  return ptr[0];
}
long double myatold(char *ptr)
{
  return strtold(ptr,0);
}
#define myatoi atoi
#define myatol atol
#define myatoll atoll
#define myatof atof


#define TMP_BUF_SZ 128
#define myvaltoa(format, val, ptrToRealloc) \
  char tmpbuf[TMP_BUF_SZ]; \
  int sz = snprintf(tmpbuf,TMP_BUF_SZ,format,val); \
  char *newPtr = realloc(ptrToRealloc,sizeof(char)*(sz+1)); \
  if(!newPtr) return 0; \
  if(sz >= (TMP_BUF_SZ)) sprintf(newPtr,format,val); \
  else memcpy(newPtr,tmpbuf,sz+1); \
  return newPtr;

char *mypchartoa(char *val, char *ptrToRealloc)
{
  myvaltoa("%s", val, ptrToRealloc);
}

char *myctoa(char val, char *ptrToRealloc)
{
  myvaltoa("%c", val, ptrToRealloc);
}

char *myitoa(int val, char *ptrToRealloc)
{
  myvaltoa("%d", val, ptrToRealloc);
}

char *myuitoa(unsigned int val, char *ptrToRealloc)
{
  myvaltoa("%u", val, ptrToRealloc);
}

char *myltoa(long val, char *ptrToRealloc)
{
  myvaltoa("%ld", val, ptrToRealloc);
}

char *myultoa(unsigned long val, char *ptrToRealloc)
{
  myvaltoa("%lu", val, ptrToRealloc);
}

char *mylltoa(long long val, char *ptrToRealloc)
{
//  myvaltoa("%"PRId64"", val, ptrToRealloc);
  myvaltoa("%lld", val, ptrToRealloc);
}

char *myulltoa(unsigned long long val, char *ptrToRealloc)
{
//  myvaltoa("%"PRIu64"", val, ptrToRealloc);
  myvaltoa("%llu", val, ptrToRealloc);
}

char *myftoa(float val, char *ptrToRealloc)
{
  myvaltoa("%f", val, ptrToRealloc);
}

char *mydtoa(double val, char *ptrToRealloc)
{
  myvaltoa("%f", val, ptrToRealloc);
}

//parece que existe um problema com long double no mingw.
//Parece que usar __mingw_printf/__mingw_sprintf/__mingw_*
//resolve o problema. Ou entao compilar com -D__USE_MINGW_ANSI_STDIO
char *myldtoa(long double val, char *ptrToRealloc)
{
  myvaltoa("%Lf", val, ptrToRealloc);
}

#define VAARG_INT8 signed int
#define VAARG_UINT8 unsigned int
#define VAARG_INT16 signed int
#define VAARG_UINT16 unsigned int
#define VAARG_INT32 signed int
#define VAARG_UINT32 unsigned int
#define VAARG_INT64 int64_t
#define VAARG_UINT64 uint64_t
#define VAARG_CHAR int
#define VAARG_UCHAR unsigned int
#define VAARG_SCHAR signed int
#define VAARG_SHORT int
#define VAARG_USHORT unsigned int
#define VAARG_SSHORT signed int
#define VAARG_INT int
#define VAARG_UINT unsigned int
#define VAARG_SINT signed int
#define VAARG_LONG long
#define VAARG_ULONG unsigned long
#define VAARG_SLONG signed long
#define VAARG_LONGLONG long long
#define VAARG_ULONGLONG unsigned long long
#define VAARG_SLONGLONG signed long long
#define VAARG_FLOAT double
#define VAARG_DOUBLE double
#define VAARG_LONGDOUBLE long double
#define VAARG_PCHAR void *
#define VAARG_PTR void *

#define STR_INT8 myatoi
#define STR_UINT8 myatoi
#define STR_INT16 myatoi
#define STR_UINT16 myatoi
#define STR_INT32 myatol
#define STR_UINT32 myatol
#define STR_INT64 myatoll
#define STR_UINT64 myatoll
#define STR_CHAR myatoc
#define STR_UCHAR myatoc
#define STR_SCHAR myatoc
#define STR_SHORT myatoi
#define STR_USHORT myatoi
#define STR_SSHORT myatoi
#define STR_INT myatoi
#define STR_UINT myatoi
#define STR_SINT myatoi
#define STR_LONG myatol
#define STR_ULONG myatol
#define STR_SLONG myatol
#define STR_LONGLONG myatoll
#define STR_ULONGLONG myatoll
#define STR_SLONGLONG myatoll
#define STR_FLOAT myatof
#define STR_DOUBLE myatof
#define STR_LONGDOUBLE myatold
#define STR_PTR (void *)myatol

#define INT8_STR myitoa
#define UINT8_STR myuitoa
#define INT16_STR myitoa
#define UINT16_STR myuitoa
#define INT32_STR myltoa
#define UINT32_STR myultoa
#define INT64_STR mylltoa
#define UINT64_STR myulltoa
#define CHAR_STR myctoa
#define UCHAR_STR myctoa
#define SCHAR_STR myctoa
#define SHORT_STR myitoa
#define USHORT_STR myuitoa
#define SSHORT_STR myitoa
#define INT_STR myitoa
#define UINT_STR myuitoa
#define SINT_STR myitoa
#define LONG_STR myltoa
#define ULONG_STR myultoa
#define SLONG_STR myltoa
#define LONGLONG_STR mylltoa
#define ULONGLONG_STR myulltoa
#define SLONGLONG_STR mylltoa
#define FLOAT_STR myftoa
#define DOUBLE_STR mydtoa
#define LONGDOUBLE_STR myldtoa
#define PTR_STR myultoa

#define BIGGER_INT_TYPE long long

void setValSTR(GenericVal *gVal, etype type, char *str)
{
  switch(type)
  {
    case T_INT8: gVal->int8Val = STR_INT8(str); break;
    case T_UINT8: gVal->uint8Val = STR_UINT8(str); break;
    case T_INT16: gVal->int16Val = STR_INT16(str); break;
    case T_UINT16: gVal->uint16Val = STR_UINT16(str); break;
    case T_INT32: gVal->int32Val = STR_INT32(str); break;
    case T_UINT32: gVal->uint32Val = STR_UINT32(str); break;
    case T_INT64: gVal->int64Val = STR_INT64(str); break;
    case T_UINT64: gVal->uint64Val = STR_UINT64(str); break;
    case T_CHAR: gVal->charVal = STR_CHAR(str); break;
    case T_UCHAR: gVal->ucharVal = STR_UCHAR(str); break;
    case T_SCHAR: gVal->scharVal = STR_SCHAR(str); break;
    case T_SHORT: gVal->shortVal = STR_SHORT(str); break;
    case T_USHORT: gVal->ushortVal = STR_USHORT(str); break;
    case T_SSHORT: gVal->sshortVal = STR_SSHORT(str); break;
    case T_INT: gVal->intVal = STR_INT(str); break;
    case T_UINT: gVal->uintVal = STR_UINT(str); break;
    case T_SINT: gVal->sintVal = STR_SINT(str); break;
    case T_LONG: gVal->longVal = STR_LONG(str); break;
    case T_ULONG: gVal->ulongVal = STR_ULONG(str); break;
    case T_SLONG: gVal->slongVal = STR_SLONG(str); break;
    case T_LONGLONG: gVal->longlongVal = STR_LONGLONG(str); break;
    case T_ULONGLONG: gVal->ulonglongVal = STR_ULONGLONG(str); break;
    case T_SLONGLONG: gVal->slonglongVal = STR_SLONGLONG(str); break;
    case T_FLOAT: gVal->floatVal = STR_FLOAT(str); break;
    case T_DOUBLE: gVal->doubleVal = STR_DOUBLE(str); printf("**%s %f\n",str,atof(str)); break;
    case T_LONGDOUBLE: gVal->longdoubleVal = STR_LONGDOUBLE(str); break;
    case T_PCHAR:
           {
             char *newPtr = mypchartoa(str, gVal->pcharVal);
             if(newPtr) gVal->pcharVal = newPtr;
           }
           break;
    case T_PTR: gVal->ptrVal = STR_PTR(str); break;
  }
}

char *getValSTR(GenericVal *gVal, etype type, char *strToRealloc)
{
  switch(type)
  {
    case T_INT8: return INT8_STR(gVal->int8Val, strToRealloc); break;
    case T_UINT8: return UINT8_STR(gVal->uint8Val, strToRealloc); break;
    case T_INT16: return INT16_STR(gVal->int16Val, strToRealloc); break;
    case T_UINT16: return UINT16_STR(gVal->uint16Val, strToRealloc); break;
    case T_INT32: return INT32_STR(gVal->int32Val, strToRealloc); break;
    case T_UINT32: return UINT32_STR(gVal->uint32Val, strToRealloc); break;
    case T_INT64: return INT64_STR(gVal->int64Val, strToRealloc); break;
    case T_UINT64: return UINT64_STR(gVal->uint64Val, strToRealloc); break;
    case T_CHAR: return CHAR_STR(gVal->charVal, strToRealloc); break;
    case T_UCHAR: return UCHAR_STR(gVal->ucharVal, strToRealloc); break;
    case T_SCHAR: return SCHAR_STR(gVal->scharVal, strToRealloc); break;
    case T_SHORT: return SHORT_STR(gVal->shortVal, strToRealloc); break;
    case T_USHORT: return USHORT_STR(gVal->ushortVal, strToRealloc); break;
    case T_SSHORT: return SSHORT_STR(gVal->sshortVal, strToRealloc); break;
    case T_INT: return INT_STR(gVal->intVal, strToRealloc); break;
    case T_UINT: return UINT_STR(gVal->uintVal, strToRealloc); break;
    case T_SINT: return SINT_STR(gVal->sintVal, strToRealloc); break;
    case T_LONG: return LONG_STR(gVal->longVal, strToRealloc); break;
    case T_ULONG: return ULONG_STR(gVal->ulongVal, strToRealloc); break;
    case T_SLONG: return SLONG_STR(gVal->slongVal, strToRealloc); break;
    case T_LONGLONG: return LONGLONG_STR(gVal->longlongVal, strToRealloc); break;
    case T_ULONGLONG: return ULONGLONG_STR(gVal->ulonglongVal, strToRealloc); break;
    case T_SLONGLONG: return SLONGLONG_STR(gVal->slonglongVal, strToRealloc); break;
    case T_FLOAT: return FLOAT_STR(gVal->floatVal, strToRealloc); break;
    case T_DOUBLE: return DOUBLE_STR(gVal->doubleVal, strToRealloc); break;
    case T_LONGDOUBLE: return LONGDOUBLE_STR(gVal->longdoubleVal, strToRealloc); break;
    case T_PCHAR: return gVal->pcharVal; break;
    case T_PTR: PTR_STR((unsigned long)gVal->ptrVal, strToRealloc); break;
  }
}

void setValV(GenericVal *gVal, etype type, va_list a_list)
{
  switch(type)
  {
    case T_INT8: gVal->int8Val = va_arg(a_list, VAARG_INT8); break;
    case T_UINT8: gVal->uint8Val = va_arg(a_list, VAARG_UINT8); break;
    case T_INT16: gVal->int16Val = va_arg(a_list, VAARG_INT16); break;
    case T_UINT16: gVal->uint16Val = va_arg(a_list, VAARG_UINT16); break;
    case T_INT32: gVal->int32Val = va_arg(a_list, VAARG_INT32); break;
    case T_UINT32: gVal->uint32Val = va_arg(a_list, VAARG_UINT32); break;
    case T_INT64: gVal->int64Val = va_arg(a_list, VAARG_INT64); break;
    case T_UINT64: gVal->uint64Val = va_arg(a_list, VAARG_UINT64); break;
    case T_CHAR: gVal->charVal = va_arg(a_list, VAARG_CHAR); break;
    case T_UCHAR: gVal->ucharVal = va_arg(a_list, VAARG_UCHAR); break;
    case T_SCHAR: gVal->scharVal = va_arg(a_list, VAARG_SCHAR); break;
    case T_SHORT: gVal->shortVal = va_arg(a_list, VAARG_SHORT); break;
    case T_USHORT: gVal->ushortVal = va_arg(a_list, VAARG_USHORT); break;
    case T_SSHORT: gVal->sshortVal = va_arg(a_list, VAARG_SSHORT); break;
    case T_INT: gVal->intVal = va_arg(a_list, VAARG_INT); break;
    case T_UINT: gVal->uintVal = va_arg(a_list, VAARG_UINT); break;
    case T_SINT: gVal->sintVal = va_arg(a_list, VAARG_SINT); break;
    case T_LONG: gVal->longVal = va_arg(a_list, VAARG_LONG); break;
    case T_ULONG: gVal->ulongVal = va_arg(a_list, VAARG_ULONG); break;
    case T_SLONG: gVal->slongVal = va_arg(a_list, VAARG_SLONG); break;
    case T_LONGLONG: printf("aqui\n"); gVal->longlongVal = va_arg(a_list, VAARG_LONGLONG); break;
    case T_ULONGLONG: gVal->ulonglongVal = va_arg(a_list, VAARG_ULONGLONG); break;
    case T_SLONGLONG: gVal->slonglongVal = va_arg(a_list, VAARG_SLONGLONG); break;
    case T_FLOAT: gVal->floatVal = va_arg(a_list, VAARG_FLOAT); break;
    case T_DOUBLE: gVal->doubleVal = va_arg(a_list, VAARG_DOUBLE); break;
    case T_LONGDOUBLE: gVal->longdoubleVal = va_arg(a_list, VAARG_LONGDOUBLE); break;
    case T_PCHAR:
           {
             char *newPtr = mypchartoa(va_arg(a_list, VAARG_PCHAR), gVal->pcharVal);
             if(newPtr) gVal->pcharVal = newPtr;
           }
           break;
    case T_PTR: gVal->ptrVal = va_arg(a_list, VAARG_PTR); break;
  }
}

void setVal(GenericVal *gVal, etype type, ...)
{
  va_list a_list;

  va_start(a_list, type);

  setValV(gVal, type, a_list);

  va_end(a_list);
}

void setVar(GenericVar *gVar, ...)
{
  va_list a_list;

  va_start(a_list, gVar);

  setValV(&gVar->val, gVar->type, a_list);

  if( ((gVar->type==T_PCHAR) && (gVar->PCHARVAL==0)) ||
      ((gVar->type==T_PCHAR) && (gVar->PTRVAL==0)) )
    gVar->isNull = 1;
  else
    gVar->isNull = 0;

  va_end(a_list);
}

void setVarNull(GenericVar *gVar)
{
  gVar->isNull = 1;
}

void setVarSTR(GenericVar *gVar, char *str)
{
  if(!str) gVar->isNull = 1;
  else
  {
    gVar->isNull = 0;
    setValSTR(&gVar->val, gVar->type, str);
  }
}

int isVarNull(GenericVar *gVar)
{
  return gVar->isNull == 1;
}

char *getVarSTR(GenericVar *gVar)
{
  if(isVarNull(gVar)) return 0;

  char *newBuf = getValSTR(&gVar->val, gVar->type, gVar->strBuf);
  if(newBuf) gVar->strBuf = newBuf;
  return newBuf;
}

void clearVar(GenericVar *gVar)
{
  if(gVar->type == T_PCHAR)
  {
    if(gVar->val.pcharVal) free(gVar->val.pcharVal);
  }
  else if(gVar->strBuf)
  {
    free(gVar->strBuf);
  }

  memset(&gVar->val, 0, sizeof(GenericVal));
  gVar->isNull = 1;
}

void delVar(GenericVar *gVar)
{
  clearVar(gVar);
  free(gVar);
}

/*
void main(void)
{
GenericVar vlonglong = GVAR(T_ULONGLONG);
GenericVar vint = GVAR(T_INT);
GenericVar vdouble = GVAR(T_DOUBLE);
GenericVar vchar = GVAR(T_CHAR);
GenericVar vpchar = GVAR(T_PCHAR);

setVarSTR(&vlonglong, "10");
setVarSTR(&vint, "10.0");
setVarSTR(&vdouble, "10.0");
setVarSTR(&vchar, "XYZ");
setVarSTR(&vpchar, "pchar");

printf("%s %d %s %f %s %c %s %s %s %llu\n",
getVarSTR(&vint), vint.INTVAL,
getVarSTR(&vdouble), vdouble.DOUBLEVAL,
getVarSTR(&vchar), vchar.CHARVAL,
getVarSTR(&vpchar), vpchar.PCHARVAL,
getVarSTR(&vlonglong), vlonglong.ULONGLONGVAL
);

setVar(&vlonglong, 11LL);
//setVar(&vlonglong, (long long)11);
setVar(&vint, 11);
setVar(&vdouble, 11.0);
setVar(&vchar, 'Y');
setVar(&vpchar, "teste");

printf("%s %d %s %f %s %c %s %s %s %llu\n",
getVarSTR(&vint), vint.INTVAL,
getVarSTR(&vdouble), vdouble.DOUBLEVAL,
getVarSTR(&vchar), vchar.CHARVAL,
getVarSTR(&vpchar), vpchar.PCHARVAL,
getVarSTR(&vlonglong), vlonglong.ULONGLONGVAL
);

clearVar(&vlonglong);
clearVar(&vint);
clearVar(&vdouble);
clearVar(&vchar);
clearVar(&vpchar);

}
*/

